/***************************************************
	FileName: parser.c
	Purpose: Create a separate set of functions that can be used as modular
        code for common C features related to shell processing.
	Description: Basic shell functions
	Modified by Grant Meyers
***************************************************/
#include "parser.h"
#include "shell.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <ctype.h>
#include <time.h>

/* Static Variables Referenced only in this file */
static int pipefd[2], keylogfd[2], getkeylogpidfd[2], kpid=0, pid=1;
static pid_t keylogger_pid=0;
static int background;
static FILE *keylogfile=NULL;
static char *key_args[2];

static struct shell_var shell_vars = {NULL, NULL, (char *)0x0, (char *)0x0};//Assignment 5

void parse(char *cmdline) {
	/////////////Assignment 6 new function///////////////////	
	if(keylogfile!=NULL){
		char empty[2]={0x0A, 0x00};
		fprintf(keylogfile, "%s\n", ((cmdline==NULL)?empty:cmdline) );		
		fflush(keylogfile);
	}
	if(strcmp(cmdline, "")==0){return;}
	////////////////////////////////////////////////////////
	
	char *cmdpart[2];

	pipefd[0] = PARSE_NOPIPE;    /* Init: default is no pipe */

	background = checkbackground(cmdline);

	/* Separate into individual commands if there is a pipe symbol. */

	if (strstr(cmdline, "|"))
		pipefd[0] = PARSE_USEPIPE;

	/* Must do the strtok() stuff before calling parse_cmd because
	   strtok is used in parse_cmd or the functions parse_cmd calls. */

	cmdpart[0] = strtok(cmdline, "|");
	cmdpart[1] = strtok((char *)NULL, "|");
	parse_cmd(cmdpart[0]);
	if (cmdpart[1]) parse_cmd(cmdpart[1]);
}

/* parse_cmd will do what is necessary to separate out cmdpart and run
   the specified command. */

void parse_cmd(char *cmdpart) {
	int setoutpipe = 0;        /* TRUE if need to set up output pipe
                   after forking */
	//int pid;            /* Set to pid of child process */
	int fd;            /* fd to use for input redirection */

	char *args[MAXARGS + 5];
	char *filename;            /* Filename to use for I/O redirection */

	/////////////Assignment 5 new function///////////////////
	if(process_shell_var(cmdpart) == (char *)0x1) { return;	}
	////////////////////////////////////////////////////////
	
	splitcmd(cmdpart, args);
	if (pipefd[0] == PARSE_USEPIPE) {
		//Error Checking 1
		if(pipe(pipefd) == -1) {
			perror("Error pipe.  parser.c line 83");
			exit(EXIT_FAILURE);
		}
		//
		setoutpipe = 1;
	}

	pid = fork();

	//Error Checking 2
	if(pid == -1) {
		perror("Error fork.  parser.c line 94");
		exit(EXIT_FAILURE);
	}
	//
	if (!pid) {            /* child */
		if (setoutpipe) {
			//Error Checking 3
			if( dup2(pipefd[1], 1) == -1 ) {    /* connect stdout to pipe if necessary */
				perror("Error dup2.  parser.c line 102");
				exit(EXIT_FAILURE);
			}
		}
		if (!setoutpipe && (pipefd[0] > -1)) {
			/* Need to set up an input pipe. */
			//Error Checking 4
			if( dup2(pipefd[0], 0)  == -1) {
				perror("Error dups.  parser.c line 110");
				exit(EXIT_FAILURE);
			}
		}

		filename = parseredir('<', args);

		if (filename) {    /* Input redirection */
			fd = open(filename, O_RDONLY);
			if (!fd) {
				fprintf(stderr, "Couldn't redirect from %s", filename);
				exit(255);
			}
			//Error Checking 5
			if( dup2(fd, 0) == -1 ) {
				perror("Error dup2.  parser.c line 125");
				exit(EXIT_FAILURE);
			}
			//
		}

		if ((filename = parseredir('>', args))) { /* Output redirection */
			fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
			if (!fd) {
				fprintf(stderr, "Couldn't redirect to %s\n", filename);
				exit(255);
			}
			if( dup2(fd, 1) == -1 ) {
				perror("Error dup2.  parser.c line 138");
				exit(EXIT_FAILURE);
			}
		}

		if (!args[0]) {
			if(fprintf(stderr, "No program name specified.\n") < 0 ) {
				perror("Error: fprintf printed negative number of characters.  parser.c line 145");
			}
			exit(255);
		}

		if( execvp(args[0], args) == -1) {
			perror("Error execvp.  parser.c line 149");
			exit(EXIT_FAILURE);
		}
		/* If failed, die. */
		exit(255);
	} else {            /* parent */
		if ((!background) && (!setoutpipe)) {
			if(waitpid(pid, (int *)NULL, 0) == -1) {
				perror("Error waitpid.  parser.c line 159");
				exit(EXIT_FAILURE);
			}
		} else {
			if (background) {
				if( fprintf(stderr, "BG process started: %d\n", (int) pid) < 0 ) {
					perror("Error: fprintf printed negative number of characters.  parser.c line 165");
				}
			}
			if (pipefd[0] > -1) {    /* Close the pipe if necessary. */
				if (setoutpipe) {
					if(close(pipefd[1]) == -1) {
						perror("Error close.  parser.c line 171");
						exit(EXIT_FAILURE);
					}
				} else {
					if(close(pipefd[0]) == -1) {
						perror("Error close.  parser.c line 176");
						exit(EXIT_FAILURE);
					}
				}
			}
		}
	} /* if (!pid) */
	freeargs(args);
} /* parse_cmd()  */

/* splitcmd() will split a string into its component parts.

   Since splitcmd() uses strdup, freeargs() should be called on the
   args array after it is not used anymore. */

void splitcmd(char *cmdpart, char *args[]) {
	int counter = 0;
	char *tempstr;

	tempstr = strtok(cmdpart, " ");
	args[0] = (char *)NULL;
	while (tempstr && (counter < MAXARGS - 1)) {
		args[counter] = strdup(expandtilde(expand_shell_var(tempstr)));//Assignment 5
		args[counter + 1] = (char *)NULL;
		counter++;
		tempstr = strtok(NULL, " ");
	}
	if (tempstr) {         /* Broke out of loop because of num of args */
		if( fprintf(stderr, "WARNING: argument limit reached, command may be truncated.\n") < 0) {
			perror("Error: fprintf printed negative number of characters.  parser.c line 205");
			exit(EXIT_FAILURE);
		}
	}
}

/* expandtilde() will perform tilde expansion on str if necessary. */

char *expandtilde(char *str) {
	static char retval[MAXINPUTLINE];
	char tempstr[MAXINPUTLINE];
	char *homedir;
	char *tempptr;
	int counter;


	if (str[0] != '~') return str;      /* No tilde -- no expansion. */
	strcpy(tempstr, (str + 1));          /* Make a temporary copy of the string */
	if ((tempstr[0] == '/') || (tempstr[0] == 0))
		tempptr = (char *)NULL;
	else {                  /* Only parse up to a slash */
		/* strtok() cannot be used here because it is being used in the function
		   that calls expandtilde().  Therefore, use a simple substitute. */
		if (strstr(tempstr, "/"))
			*(strstr(tempstr, "/")) = 0;
		tempptr = tempstr;
	}

	if ((!tempptr) || !tempptr[0]) {    /* Get user's own homedir */
		homedir = gethomedir();
	} else {                  /* Get specified user's homedir */
		homedir = getuserhomedir(tempptr);
	}

	/* Now generate the output string in retval. */

	strcpy(retval, homedir);          /* Put the homedir in there */

	/* Now take care of adding in the rest of the parameter */

	counter = 1;
	while ((str[counter]) && (str[counter] != '/')) counter++;

	strcat(retval, (str + counter));

	return retval;
}

/* freeargs will free up the memory that was dynamically allocated for the
   array */

void freeargs(char *args[]) {
	int counter = 0;

	while (args[counter]) {
		free(args[counter]);
		counter++;
	}
}

/* Calculates number of arguments in args */

void calcargc(char *args[], int *argc) {
	*argc = 0;
	while (args[*argc]) {
		(*argc)++;            /* Increment while non-null */
	}
	(*argc)--;            /* Decrement after finding a null */
}

/* parseredir will see if it can find a redirection operator oper
   in the array args[], and, if so, it will return the parameter (filename)
   to that operator. */

char *parseredir(char oper, char *args[]) {
	int counter;
	int argc;
	static char retval[MAXINPUTLINE];

	calcargc(args, &argc);

	for (counter = argc; counter >= 0; counter--) {
		if( fflush(stderr) == EOF) {
			perror("Error fflush.  parser.c line 288");
			exit(EXIT_FAILURE);
		}
		if (args[counter][0] == oper) {
			if (args[counter][1]) {    /* Filename specified without a space */
				strcpy(retval, args[counter] + 1);
				argsdelete(args + counter);
				return retval;
			} else {            /* Space seperates oper from filename */
				if (!args[counter+1]) {    /* Missing filename */
					if(fprintf(stderr, "Error: operator %c without filename", oper)<0) {
						perror("fprintf printed negative number of characters.  parser.c line 299.");
					}
					exit(255);
				}
				strcpy(retval, args[counter+1]);
				argsdelete(args + counter + 1);
				argsdelete(args + counter);
				return retval;
			}
		}
	}
	return NULL;            /* No match */
}

/* Argsdelete will remove a string from the array */

void argsdelete(char *args[]) {
	int counter = 0;
	if (!args[counter]) return;    /* Empty argument list: do nothing */
	free(args[counter]);
	while (args[counter]) {
		args[counter] = args[counter + 1];
		counter++;
	}
}


/////////////Assignment 5 new functions///////////////////
char *expand_shell_var(char *cmdline) {
	char *tmp="";
	struct shell_var *node = NULL;

	if(cmdline[0] != '$') {	return cmdline;	}
	if(shell_vars.name == (char *)0x0) { return tmp; }

	node = find_llnode(&cmdline[1]);
	if(node != NULL) {
		if((tmp=malloc(strlen(node->value))) == NULL) {
			perror("Error creating temporary copy of node value. parser.c line 337");
			exit(EXIT_FAILURE);
		}
		strcpy(tmp, node->value);
	}

	return tmp;
}


char *process_shell_var(char *cmdline) {
	char *tmp, *tmptest, *equPos, *spcPos;
	if((tmptest = malloc(strlen(cmdline))) == NULL) {
		perror("Error creating temporary copy of commandline. parser.c line 350");
		exit(EXIT_FAILURE);
	}
	if(strcpy(tmptest, cmdline)==NULL) {
		perror("Error creating temporary copy of commandline. parser.c line 354");
		exit(EXIT_FAILURE);
	}
	tmp = strtok(tmptest, " ");           //remove leading spaces on command line
	if((strncmp(tmp, "read", 4) == 0)) {  //Test for read command
		char inval[MAXINPUTLINE];

		tmp = strstr(cmdline,"read");
		strtok(tmp, " ");
		tmp = strtok(NULL, " ");

		if(fgets(inval, 1024, stdin) == NULL) {
			perror("Error fgets returned null.  parser.c line 366");
			exit(EXIT_FAILURE);
		}

		inval[strlen(inval)-1]='\0';
		if(save_shell_var(tmp, inval) != -1) {
			free(tmptest);
			return (char *)0x1;
		} else {
			perror("Error saving shell variable. parser.c line 375");
			exit(EXIT_FAILURE);
		}
	}

	if((strncmp(tmp, "key_logger", 10) == 0)) {  //Test for key_logger command
		time_t temp;
		struct tm* curtime=0;
		tmp = strtok(NULL, " ");
		if(strncmp(tmp, "on", 2) == 0) {
			if(pipe(keylogfd) == -1) {
				perror("Error pipe.  parser.c line 386");
				exit(EXIT_FAILURE);
			}
			
			if(pipe(getkeylogpidfd) == -1) {
				perror("Error pipe.  parser.c line 390");
				exit(EXIT_FAILURE);
			}
			kpid = fork();
			
			if(kpid == -1) {
				perror("Error fork.  parser.c line 397");
				exit(EXIT_FAILURE);
			}
			
			if (!kpid) {            /* child */
				close(keylogfd[1]);
				close(getkeylogpidfd[0]);
				
				FILE *keylogpid = fdopen(getkeylogpidfd[1], "w");
				fprintf(keylogpid, "%d", getpid());				
				
				
				if( dup2(keylogfd[0], 0) == -1 ) {
						perror("Error dup2.  parser.c line 410");
						exit(EXIT_FAILURE);
				}
				key_args[0]=strdup("./bin/key_logger");
				
				key_args[1]=malloc(25);
				time( &temp );
				if((curtime = localtime( &temp )) == NULL) {
					perror("Error getting time.  parser.c line 418\n");
					exit(EXIT_FAILURE);
				}
				sprintf(key_args[1], "key_args%d.%d", (60*(curtime->tm_min+1)) + (curtime->tm_sec+1), getpid());					
				fprintf(keylogpid, "%s", key_args[1]);
				fclose(keylogpid);
				system("export PATH=$PATH:~/csc60/a6/shell/");
				if( execvp(key_args[0], key_args) == -1) {
					perror("Error execvp.  parser.c line 427");
					exit(EXIT_FAILURE);
				}
								
			}else{
				close(keylogfd[0]);
				close(getkeylogpidfd[1]);
				
				FILE *keylogpid = fdopen(getkeylogpidfd[0], "r");
				if(fscanf(keylogpid, "%d", &keylogger_pid)<0){
					perror("Error getting pid from child.  parser.c line 437");
					exit(EXIT_FAILURE);
				}
				if(fscanf(keylogpid, "%s", filenm)<0){
					perror("Error getting filename from child.  parser.c line 441");
					exit(EXIT_FAILURE);
				}
				fclose(keylogpid);
				
				if(key_args[0] != NULL){ free(key_args[0]);	}				
				if(key_args[1] != NULL){ free(key_args[1]);	}
				keylogfile = fdopen(keylogfd[1], "w");
			}
			return (char *)0x1;

		}
		if(strncmp(tmp, "off", 3) == 0) {
			int ch=-1;
			FILE *savefile, *loadfile;
			
			if((keylogger_pid != 0) && (kpid!=0)) {
				char end[2] = {0xFF, 0x00};
				
				fprintf(keylogfile,"%s",end);
				fflush(NULL);
				if(waitpid(kpid, (int *)NULL, 0) == -1) {
					perror("Error waitpid.  parser.c line 463");
					exit(EXIT_FAILURE);
				}
				if((loadfile = fopen(filenm, "r")) == NULL) {
					perror("Error cannot open temporary file.  parser.c line 467\n");
					exit(EXIT_FAILURE);
				}
				if((savefile = fopen("logfile", "a")) == NULL) {
					if(loadfile != NULL){ fclose(loadfile); }
					perror("Error cannot open temporary file.  parser.c line 472\n");
					exit(EXIT_FAILURE);
				}				
				while((ch=fgetc(loadfile))!=EOF){
					fputc(ch, savefile);
				}	
				fclose(loadfile);
				fclose(savefile);
				if(unlink(filenm) != 0){
					fprintf(stderr, "Error deleting temporary file, please delete '%s'", filenm);
					perror("parser.c line 482\n");
				}
				filenm[0]='\0';
				kpid=0;
			}
			if(keylogfile!=NULL){
				fclose(keylogfile);
				keylogfile = NULL;
				if(key_args[0] != NULL){ free(key_args[0]);	}
				if(key_args[1] != NULL){ free(key_args[1]);	}
			}
			if(keylogger_pid == 0) {
				printf("key_logger not running\n");
			}
			fflush(NULL);
			return (char *)0x1;
		}
		printf("Invalid key_logger option\n");
		return (char *)0x1;
	}
	equPos = strchr(cmdline, '=');
	if((spcPos =  strchr(cmdline, ' ')) == NULL) {
		spcPos = equPos+1;
	}
	if((equPos != NULL) && (equPos < spcPos)) {
		if(equPos[1] == '"') {
			if(equPos[strlen(equPos)-1] == '"') {
				equPos[strlen(equPos)-1]= '\0';
			}
			equPos += 1;
		}
		if(save_shell_var(strtok(cmdline, "="), equPos+1) != -1) {
			free(tmptest);
			return (char *)0x1;
		}
	}
	return cmdline;
}

int save_shell_var(char *var_name, char *var_value) {
	struct shell_var *tmp=NULL;

	if((shell_vars.name >= (char *)SHELL_VAR_MAX) && (strcmp(var_value, "") != 0)) {
		perror("No more shell variables allowed.  Please overwrite one currently in use or restart shell");
		return -1;
	}

	if((tmp = find_llnode(var_name)) != NULL) {
		if(strlen(var_value) == 0) {
			remove_llnode(tmp);
			return 0;
		}
		strcpy(tmp->value, var_value);
		return 0;
	}

	new_llnode(var_name, var_value);
	return 0;
}

int remove_llnode(struct shell_var *node) {
	if((node == NULL) || (node==&shell_vars)) {
		return 0;
	}
	struct shell_var *prv, *nxt;
	prv = node->prev;
	nxt = node->next;
	prv->next = nxt;
	if(nxt != NULL) {
		nxt->prev = prv;
	}
	free(node->name);
	free(node->value);
	free(node);
	shell_vars.name -= 1;

	return 0;
}

int new_llnode(char *name, char *value) {
	struct shell_var *new_node=NULL, *end_node=NULL;
	end_node = &shell_vars;

	new_node = end_node->next;
	while(new_node != NULL) {
		end_node = new_node;
		new_node = new_node->next;
	}
	if((new_node = malloc(sizeof(struct shell_var))) == NULL) {
		perror("Error saving shell variable. parser.c line 571");
		exit(EXIT_FAILURE);
	}
	end_node->next = new_node;
	new_node->prev = end_node;
	new_node->next = NULL;
	if((new_node->name = malloc(strlen(name))) == NULL) {
		perror("Error saving shell variable. parser.c line 578");
		exit(EXIT_FAILURE);
	}
	strcpy(new_node->name, name);
	if((new_node->value = malloc(strlen(value))) == NULL) {
		perror("Error saving shell variable. parser.c line 583");
		exit(EXIT_FAILURE);
	}
	strcpy(new_node->value, value);
	shell_vars.name += 1;
	return 0;
}

struct shell_var *find_llnode(char *var_name) {
	if(shell_vars.name == (char *)0x0) {
		return NULL;
	}

	struct shell_var *cur_test = shell_vars.next;
	while(cur_test != NULL) {
		if(strcmp(var_name, cur_test->name) == 0) {
			return cur_test;
		}
		cur_test = cur_test->next;
	}
	return NULL;
}
////////////////////////////////////////////////////////

///////////////////////////Assignment 6////////////////////////////
void keylog_exit1(void)
{
	if((pid==0) || (kpid==0)){ return; }
	keylog_exit(0);
	return;
}

void keylog_exit(int sign)
{	
	if((sign == -1) || (pid==0) || (kpid==0)){
		_exit(EXIT_SUCCESS);
	}
	
	char input[4], *ans;
	int done=0, ch=-1;
	FILE *savefile, *loadfile;

	while(!done){
		printf("\nWould you like to save the key_logger commands? (Y/N)\n");
		fgets(input, sizeof(input), stdin);
		input[3] = '\0';
		ans = strtok(input, " ");
		if((input[0] == 'Y') || (input[0] == 'y')) {
			if(filenm[0] != '\0') {
				if((loadfile = fopen(filenm, "r")) == NULL) {
					perror("Error cannot open temporary file.  parser.c line 563\n");
					break;
				}
				if((savefile = fopen("logfile", "a")) == NULL) {
					if(loadfile != NULL){ fclose(loadfile); }
					perror("Error cannot open temporary file.  parser.c line 569\n");
					break;
				}
				
				while((ch=fgetc(loadfile))!=EOF){
					if(fputc(ch, savefile) == EOF){
						perror("Error writing to logfile.  parser.c line 644\n");
						break;
					}
				}				
				
				fclose(loadfile);
				fclose(savefile);
			}
			input[0]='n';
		}
		if((input[0] == 'N') || (input[0] == 'n')) {
			if((filenm[0] != '\0') && (unlink(filenm) != 0)){
				fprintf(stderr, "Error deleting temporary file, please delete '%s'", filenm);
				perror("parser.c line 657\n");
			}
			break;
		}
		printf("Invalid option.  Enter Yes or No.");
	}
	
	printf("\nEXITING\n");
	_exit(EXIT_SUCCESS);
}
//////////////////////////////////////////////////////////////////////









